package base;

import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Map用法
 * <p>
 * * {@link Map#clear()}                               删除 hashMap 中的所有键/值对
 * * {@link Map#clone()}                               复制一份 hashMap
 * * {@link Map#isEmpty()}                             判断 hashMap 是否为空
 * * {@link Map#size()}                                计算 hashMap 中键/值对的数量
 * * {@link Map#put(Object, Object)} 	               将键/值对添加到 hashMap 中
 * * {@link Map#putAll(Map)} 	                       将所有键/值对添加到 hashMap 中
 * * {@link Map#putIfAbsent(Object, Object)} 	       如果 hashMap 中不存在指定的键，则将指定的键/值对插入到 hashMap 中
 * * {@link Map#remove(Object)} 	                   删除 hashMap 中指定键 key 的映射关系
 * * {@link Map#containsKey(Object)} 	               检查 hashMap 中是否存在指定的 key 对应的映射关系
 * * {@link Map#containsValue(Object)}                 检查 hashMap 中是否存在指定的 value 对应的映射关系
 * * {@link Map#replace(Object, Object)} 	           替换 hashMap 中是指定的 key 对应的 value
 * * {@link Map#replaceAll(BiFunction)} 	           将 hashMap 中的所有映射关系替换成给定的函数所执行的结果
 * * {@link Map#get(Object)} 	                       获取指定 key 对应对 value
 * * {@link Map#getOrDefault(Object, Object)} 	       获取指定 key 对应对 value，如果找不到 key ，则返回设置的默认值
 * * {@link Map#forEach(BiConsumer)} 	               对 hashMap 中的每个映射执行指定的操作
 * * {@link Map#entrySet()}                            返回 hashMap 中所有映射项的集合集合视图
 * * {@link Map#keySet()}                              返回 hashMap 中所有 key 组成的集合视图
 * * {@link Map#values()}                              返回 hashMap 中存在的所有 value 值
 * * {@link Map#merge(Object, Object, BiFunction)}     添加键值对到 hashMap 中
 * * {@link Map#compute(Object, BiFunction)}           对 hashMap 中指定 key 的值进行重新计算
 * * {@link Map#computeIfAbsent(Object, Function)}     对 hashMap 中指定 key 的值进行重新计算，如果不存在这个 key，则添加到 hasMap 中
 * * {@link Map#computeIfPresent(Object, BiFunction)}  对 hashMap 中指定 key 的值进行重新计算，前提是该 key 存在于 hashMap 中
 * * {@link Map#replace(Object, Object)}
 * * {@link Map#replace(Object, Object, Object)}
 * * {@link Map#of(Object, Object)}
 * * {@link Map#ofEntries(Map.Entry[])}
 * * {@link Map#copyOf(Map)}
 * </p>
 *
 * @author 王霄
 * @date 2021/9/11 14:42
 */
public class MapDemo {
    public static void main(String[] args) {
        var map = new HashMap<String, Integer>(2);

        //put与compute都是直接覆盖,不同的是compute参数是函数
        System.out.println(map.put("A", 100));
        System.out.println(map.put("A", 1000));
        System.out.println(map.compute("A", (k, v) -> Optional.ofNullable(v).map(x -> x * x).orElse(0)));
        System.out.println(map.compute("B", (k, v) -> Optional.ofNullable(v).map(x -> x * x).orElse(0)));
        System.out.println(map);

        //putIfAbsent与computeIfAbsent都是不存在才新增,存在不处理,不同的是computeIfAbsent参数是函数
        System.out.println(map.putIfAbsent("B", 99));
        System.out.println(map.computeIfAbsent("C", k -> 100));
        System.out.println(map);

        //computeIfPresent只对存在的key操作
        System.out.println(map.computeIfPresent("D", (k, v) -> 777));
        System.out.println(map);
        System.out.println(map.computeIfPresent("A", (k, v) -> 666));
        System.out.println(map);

        var computeMap = new HashMap<String, Integer>();
        computeMap.put("A", 10);
        System.out.println(computeMap);
        computeMap.compute("A", (k, v) -> {
            if (v == null) v = 1000;
            else if (v < 100) v = 100;
            return v;
        });
        System.out.println(computeMap);

        //#################################################################################################
        //练习: 求出每个学校中的每个班中成绩最高的人
        List<Student> students = Student.initList();

        //方法1
        Map<String, Map<String, Student>> sMap = new HashMap<>();
        students.forEach(s -> sMap.compute(s.schoolId, (k1, v1) -> {
            if (v1 == null) {
                v1 = new HashMap<>(4);
                v1.put(s.classroomId, s);
            } else {
                v1.compute(s.classroomId, (k2, v2) -> {
                    if (v2 == null || v2.scope < s.scope) {
                        return s;
                    }
                    return v2;
                });
            }
            return v1;
        }));
        System.out.println(JSONObject.toJSONString(sMap));

        //方法2
        Map<String, Map<String, Student>> s2Map = students.stream()
                .collect(Collectors.groupingBy(Student::getSchoolId,
                        Collectors.groupingBy(Student::getClassroomId,
                                Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparing(Student::getScope)),
                                        x -> x.orElse(null)))));
        System.out.println(JSONObject.toJSONString(s2Map));

        //方法3
        Map<String, Map<String, Student>> s3Map = new HashMap<>();
        for (Student student : students) {
            Map<String, Student> tempMap = s3Map.get(student.schoolId);
            if (tempMap == null) {
                tempMap = new HashMap<>();
                tempMap.put(student.classroomId, student);
                s3Map.put(student.schoolId, tempMap);
            } else {
                Student old = tempMap.get(student.schoolId);
                if (old == null || old.scope < student.scope) {
                    tempMap.put(student.classroomId, student);
                    tempMap.put(student.classroomId, student);
                    s3Map.put(student.schoolId, tempMap);
                }
            }
        }
        System.out.println(JSONObject.toJSONString(s3Map));


        //求出每个学校中每个班的总人数
        Map<String, Map<String, Long>> countMap = students.stream()
                .collect(Collectors.groupingBy(Student::getSchoolId,
                        Collectors.groupingBy(Student::getClassroomId, Collectors.counting())));


        Map<String, List<String>> collect = students.stream()
                .collect(Collectors.groupingBy(Student::getSchoolId,
                        Collectors.mapping(Student::getSchoolId, Collectors.toList())));

        System.out.println(countMap);

        IpBox ipBox = new IpBox();
        List<Bind> oldBinds = List.of(new Bind("A", List.of("1", "2", "3")),
                new Bind("B", List.of("4", "5", "6")));
        Map<String, List<Bind>> bindMap = new HashMap<>();
        bindMap.put("key", oldBinds);
        ipBox.setBinds(bindMap);
        List<Bind> binds = List.of(new Bind("A", List.of("9", "9", "9")));
        if (Objects.isNull(ipBox.getBinds())) {
            ipBox.setBinds(new HashMap<>(2));
        }
        ipBox.getBinds().merge("key", binds, (v1, v2) -> Stream
                .concat(v1.stream().filter(x -> !x.getKey().equals("A")),
                        v2.stream())
                .collect(Collectors.toList()));
        System.out.println(ipBox.getBinds());

    }

    /**
     * 学生类
     */
    @AllArgsConstructor
    @NoArgsConstructor
    @Data
    public static class Student {
        @JSONField(serialize = false)
        private String schoolId;
        @JSONField(serialize = false)
        private String classroomId;
        private String name;
        private int scope;

        public static List<Student> initList() {
            List<Student> students = new ArrayList<>();
            students.add(new Student("学校01", "1班", "小A", 66));
            students.add(new Student("学校01", "1班", "小B", 99));
            students.add(new Student("学校01", "1班", "小D", 77));
            students.add(new Student("学校01", "2班", "小T", 55));
            students.add(new Student("学校01", "2班", "小H", 80));
            students.add(new Student("学校01", "3班", "小V", 98));
            students.add(new Student("学校01", "3班", "小N", 91));
            students.add(new Student("学校01", "3班", "小M", 88));
            students.add(new Student("学校02", "1班", "小J", 16));
            students.add(new Student("学校02", "1班", "小K", 32));
            students.add(new Student("学校02", "2班", "小U", 54));
            students.add(new Student("学校02", "2班", "小O", 66));
            students.add(new Student("学校02", "3班", "小P", 67));
            students.add(new Student("学校02", "3班", "小L", 87));
            return students;
        }

        @Override
        public String toString() {
            return "Student{" +
                    "schoolId='" + schoolId + '\'' +
                    ", classroomId='" + classroomId + '\'' +
                    ", name='" + name + '\'' +
                    ", scope=" + scope +
                    '}';
        }
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Bind {
        private String key;
        private List<String> values;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class IpBox {
        Map<String, List<Bind>> binds;
    }
}
